class MyWebSocket {
  // url: 地址(例如：'ws://127.0.0.1:8000/ws/chat/root/')
  // autoReconnect：true/false 断线是否自动进行重新连接
  // ping：true/false 是否主动进行网络连通性探测
  // pingInterval：网络连通性主动探测时间间隔，单位为秒
  // debug：true/false 是否进行调试，调试模式在控制台进行打印输出
  // event默认事件说明：open: 连接成功事件  close: 断开连事件
  constructor(url = null, autoReconnect = true, ping = true, pingInterval = 10, debug = true) {
    // Array.prototype.remove = (val)=>{   // Array添加remove方法
    //     let index = this.indexof(val)
    //     if(index !== -1) {
    //         this.splice(index, 1)
    //     }
    // }
    this.url = url
    this.autoReconnect = autoReconnect
    this.ping = ping
    this.pingInterval = pingInterval
    this.debug = debug

    this.pingTimer = null
    this.wait_dict = {}   // 会话等待字典 key: id(由前端生成的会话标识ID)  value: {timer: 会话超时定时器, resolve: Promise的resolve函数}
    this.event_dict = {}  // 绑定事件字典 key: event(注册的事件)  value: function(注册事件的回调函数)
    this.close_state = false
    this._init()
  }

  _init = () => {
    this.ws = new WebSocket(this.url)
    this.ws.onopen = this._onopen
    this.ws.onclose = this._onclose
    this.ws.onerror = this._onerror
    this.ws.onmessage = this._onmessage
    if (this.ping) {
      if (this.pingTimer !== null) {
        clearInterval(this.pingTimer)
        this.pingTimer = null
      }
      this.pingTimer = setInterval(this._ping, this.pingInterval * 1000)  // 若放到constructor内，websocket连接异常会导致定时器失效
    }
  }
  _reconnect = () => {
    setTimeout(this._init, 1000)  // 防止同步方式websocket连接失败影响运行
  }
  _onopen = () => {
    if (Object.prototype.hasOwnProperty.call(this.event_dict, "open")) {
      this.event_dict['open']()
    }
  }
  _onclose = () => {
    clearInterval(this.pingTimer)
    if (Object.prototype.hasOwnProperty.call(this.event_dict, "close")) {
      this.event_dict['close']()
    }
    if (this.autoReconnect && (this.close_state===false)) {
      this._reconnect()
    }
  }
  _onerror = () => {
    return null
  }
  _get_id = () => {
    let date = new Date()
    return date.getTime()
  }
  _ping = async () => {
    try {
      if (this.ws.readyState === this.ws.OPEN) {
        let data = await this.send_text({event: 'ping', waitResponse: true, timeout: 5})
        if (data !== 'pong') {
          throw new Error('ping timeout')
        }
      }
    } catch (e) {
      if (this.ws.readyState !== this.ws.CLOSED) {
        this.ws.close()
      }
    }
  }
  _onmessage = (e) => {
    let data = JSON.parse(e.data)
    if (this.debug) {
      console.log('websocket接收：', data)
    }
    if (Object.prototype.hasOwnProperty.call(data, "id")) {
      if (Object.prototype.hasOwnProperty.call(this.wait_dict, data.id)) {
        clearTimeout(this.wait_dict[data.id].timer)
        this.wait_dict[data.id].resolve(data.data)
        delete this.wait_dict[data.id]
      }
    }
    if (Object.prototype.hasOwnProperty.call(data, "event")) {
      if (Object.prototype.hasOwnProperty.call(this.event_dict, data.event)) {
        this.event_dict[data.event](data.data)
      }
    }
  }
  on = ({event, function:fun}) => {
    this.event_dict[event] = fun
  }
  send_text = ({event, data=null, dataType='string', waitResponse=false, timeout=10}) => {
    if (waitResponse) {
      return new Promise((resolve, reject) => {
        let id = this._get_id()
        let timer = setTimeout(() => {
          delete this.wait_dict[id]
          reject(`websocket response timeout!, request id:${id}`)
        }, timeout * 1000)
        this.wait_dict[id] = {resolve, timer}
        let send_data = {id, event, data, dataType}
        this.ws.send(JSON.stringify(send_data))
        if (this.debug) {
          console.log('websocket发送：', send_data)
        }
      })
    } else {
      let send_data = {event, data, dataType}
      this.ws.send(JSON.stringify(send_data))
      if (this.debug) {
        console.log('websocket发送：', send_data)
      }
    }
  }
  send_bytes = (data) => {
    this.ws.binaryType = 'arraybuffer'
    this.ws.send(data)
  }
  close = () => {
    this.close_state = true
    this.ws.close()
  }
}

export default MyWebSocket
